package compilers.AST.expression;

import compilers.AST.Node;
import compilers.AST.types.MyType;
import compilers.AST.types.TypeSpecifier;
import compilers.compiler.PGenerator;
import compilers.exceptions.SemanticalException;
import compilers.symbolTable.SymbolTableScope;

public class Unary extends Node {
	
	public final static int PLUS = 1;
	public final static int MINUS = 2;

	private int fSign;
	private Negation fNegation;
	
	public Unary() {
		fSign = PLUS;
	}
	
	public Unary(int lineNr) {
		super(lineNr);
		fSign = PLUS;
	}
	
	public void print(int identLevel) {
		println(identLevel, "Unary");
		if (fSign == MINUS) {
			println(identLevel+1, "Sign = -");
		}
		fNegation.print(identLevel+1);
	}
	
	public void checkSemantics(SymbolTableScope currentScope) throws SemanticalException {
		fNegation.checkSemantics(currentScope);
		if (fSign == MINUS) {
			if ( (fNegation.getResultType(currentScope) != TypeSpecifier.INTEGER) && (fNegation.getResultType(currentScope) != TypeSpecifier.FLOAT)) {
				throw new SemanticalException("The type in the unary expression is not of the requested numerical types.", fLineNr);
			}
		}
	}
	
	public void setSign(int sign) {
		fSign = sign;
	}
	
	public void setNegation(Negation negation) {
		fNegation = negation;
	}
	
	public int getResultType(SymbolTableScope currentScope) {
		return fNegation.getResultType(currentScope);
	}
	
	public int getOriginalType(SymbolTableScope currentScope) {
		return fNegation.getOriginalType(currentScope);
	}
	
	public boolean isConstant(SymbolTableScope currentScope) {
		return fNegation.isConstant(currentScope);
	}

	public boolean isArray(SymbolTableScope currentScope) {
		if (fSign == PLUS) {
			return fNegation.isArray(currentScope);
		} else {
			return false;
		}
	}
	
	public String getArrayIdent(SymbolTableScope currentScope) {
		return fNegation.getArrayIdent(currentScope);
	}
	
	public MyType calculateValue(SymbolTableScope currentScope) {
		if(fSign == PLUS) {
			return fNegation.calculateValue(currentScope);
		} else {
			return fNegation.calculateValue(currentScope).negate();
		}
	}
	
	public void generatePcode(PGenerator pGen, SymbolTableScope currentScope) {
		TypeSpecifier resultType = new TypeSpecifier(fNegation.getResultType(currentScope));
		if (fNegation.isConstant(currentScope)) {
			pGen.load(resultType.getPType(currentScope), fNegation.calculateValue(currentScope).valueToString());
		} else {
			fNegation.generatePcode(pGen, currentScope);
		}
		if (fSign == MINUS) {
			pGen.negate(resultType.getPType(currentScope));
		}
	}
}
